{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Project Solution: Goal 2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here is the final `Polygon` class we ended up with in goal 1:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "class Polygon:\n",
    "    def __init__(self, n, R):\n",
    "        if n < 3:\n",
    "            raise ValueError('Polygon must have at least 3 vertices.')\n",
    "        self._n = n\n",
    "        self._R = R\n",
    "        \n",
    "    def __repr__(self):\n",
    "        return f'Polygon(n={self._n}, R={self._R})'\n",
    "    \n",
    "    @property\n",
    "    def count_vertices(self):\n",
    "        return self._n\n",
    "    \n",
    "    @property\n",
    "    def count_edges(self):\n",
    "        return self._n\n",
    "    \n",
    "    @property\n",
    "    def circumradius(self):\n",
    "        return self._R\n",
    "    \n",
    "    @property\n",
    "    def interior_angle(self):\n",
    "        return (self._n - 2) * 180 / self._n\n",
    "\n",
    "    @property\n",
    "    def side_length(self):\n",
    "        return 2 * self._R * math.sin(math.pi / self._n)\n",
    "    \n",
    "    @property\n",
    "    def apothem(self):\n",
    "        return self._R * math.cos(math.pi / self._n)\n",
    "    \n",
    "    @property\n",
    "    def area(self):\n",
    "        return self._n / 2 * self.side_length * self.apothem\n",
    "    \n",
    "    @property\n",
    "    def perimeter(self):\n",
    "        return self._n * self.side_length\n",
    "    \n",
    "    def __eq__(self, other):\n",
    "        if isinstance(other, self.__class__):\n",
    "            return (self.count_edges == other.count_edges \n",
    "                    and self.circumradius == other.circumradius)\n",
    "        else:\n",
    "            return NotImplemented\n",
    "        \n",
    "    def __gt__(self, other):\n",
    "        if isinstance(other, self.__class__):\n",
    "            return self.count_vertices > other.count_vertices\n",
    "        else:\n",
    "            return NotImplemented"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we need to create a sequence type that will return these Polygons, starting with 3 vertices, up to (and including) a polygon of `m` sides.\n",
    "\n",
    "Our sequence type will need to implement:\n",
    "* a `__len__` method\n",
    "* a `__getitem__` method\n",
    "* a method that identifies the polygon with largest area to perimeter ratio: let's call it `max_efficiency_polygon` - note that the Polygon class does not have an `efficiency` method, so we'll have to calculate it outside of the Polygon class."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's start with some of the basics:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygons:\n",
    "    def __init__(self, m, R):\n",
    "        if m < 3:\n",
    "            raise ValueError('m must be greater than 3')\n",
    "        self._m = m\n",
    "        self._R = R\n",
    "        \n",
    "    def __len__(self):\n",
    "        return self._m - 2\n",
    "    \n",
    "    def __repr__(self):\n",
    "        return f'Polygons(m={self._m}, R={self._R})'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's make sure this works as intended:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "m must be greater than 3",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-3-449d2b183c29>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mpolygons\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mPolygons\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m10\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-2-c384006bada7>\u001b[0m in \u001b[0;36m__init__\u001b[1;34m(self, m, R)\u001b[0m\n\u001b[0;32m      2\u001b[0m     \u001b[1;32mdef\u001b[0m \u001b[0m__init__\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mself\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mm\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mR\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      3\u001b[0m         \u001b[1;32mif\u001b[0m \u001b[0mm\u001b[0m \u001b[1;33m<\u001b[0m \u001b[1;36m3\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m             \u001b[1;32mraise\u001b[0m \u001b[0mValueError\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'm must be greater than 3'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      5\u001b[0m         \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_m\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mm\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      6\u001b[0m         \u001b[0mself\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_R\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mR\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mValueError\u001b[0m: m must be greater than 3"
     ]
    }
   ],
   "source": [
    "polygons = Polygons(2, 10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That's exactly what we want, so that's good."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "polygons = Polygons(3, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(polygons)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "polygons = Polygons(6, 1)\n",
    "len(polygons)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's also test the representation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygons(m=6, R=1)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "polygons"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " Let's now implement a list that will contain all the polygons.\n",
    " \n",
    " We'll do that in the `__init__` method as well."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygons:\n",
    "    def __init__(self, m, R):\n",
    "        if m < 3:\n",
    "            raise ValueError('m must be greater than 3')\n",
    "        self._m = m\n",
    "        self._R = R\n",
    "        self._polygons = [Polygon(i, R) for i in range(3, m+1)]\n",
    "        \n",
    "    def __len__(self):\n",
    "        return self._m - 2\n",
    "    \n",
    "    def __repr__(self):\n",
    "        return f'Polygons(m={self._m}, R={self._R})'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before we can test this, we need to implement the `__getitem__` method:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygons:\n",
    "    def __init__(self, m, R):\n",
    "        if m < 3:\n",
    "            raise ValueError('m must be greater than 3')\n",
    "        self._m = m\n",
    "        self._R = R\n",
    "        self._polygons = [Polygon(i, R) for i in range(3, m+1)]\n",
    "        \n",
    "    def __len__(self):\n",
    "        return self._m - 2\n",
    "    \n",
    "    def __repr__(self):\n",
    "        return f'Polygons(m={self._m}, R={self._R})'\n",
    "    \n",
    "    def __getitem__(self, s):\n",
    "        return self._polygons[s]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice how easy it was using delegation to the underlying list of polygons!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's test this out:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "polygons = Polygons(8, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Polygon(n=3, R=1)\n",
      "Polygon(n=4, R=1)\n",
      "Polygon(n=5, R=1)\n",
      "Polygon(n=6, R=1)\n",
      "Polygon(n=7, R=1)\n",
      "Polygon(n=8, R=1)\n"
     ]
    }
   ],
   "source": [
    "for p in polygons:\n",
    "    print(p)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Polygon(n=5, R=1)\n",
      "Polygon(n=6, R=1)\n",
      "Polygon(n=7, R=1)\n"
     ]
    }
   ],
   "source": [
    "for p in polygons[2:5]:\n",
    "    print(p)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Polygon(n=8, R=1)\n",
      "Polygon(n=7, R=1)\n",
      "Polygon(n=6, R=1)\n",
      "Polygon(n=5, R=1)\n",
      "Polygon(n=4, R=1)\n",
      "Polygon(n=3, R=1)\n"
     ]
    }
   ],
   "source": [
    "for p in polygons[::-1]:\n",
    "    print(p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We still need to implement a method that identifies the polygon with highest area:perimeter ratio."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Polygons:\n",
    "    def __init__(self, m, R):\n",
    "        if m < 3:\n",
    "            raise ValueError('m must be greater than 3')\n",
    "        self._m = m\n",
    "        self._R = R\n",
    "        self._polygons = [Polygon(i, R) for i in range(3, m+1)]\n",
    "        \n",
    "    def __len__(self):\n",
    "        return self._m - 2\n",
    "    \n",
    "    def __repr__(self):\n",
    "        return f'Polygons(m={self._m}, R={self._R})'\n",
    "    \n",
    "    def __getitem__(self, s):\n",
    "        return self._polygons[s]\n",
    "    \n",
    "    @property\n",
    "    def max_efficiency_polygon(self):\n",
    "        sorted_polygons = sorted(self._polygons, \n",
    "                                 key=lambda p: p.area/p.perimeter,\n",
    "                                reverse=True)\n",
    "        return sorted_polygons[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "polygons = Polygons(10, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Polygon(n=10, R=1)"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "polygons.max_efficiency_polygon"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's test this to make sure that is correct:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(Polygon(n=3, R=1), 0.25000000000000006),\n",
       " (Polygon(n=4, R=1), 0.35355339059327384),\n",
       " (Polygon(n=5, R=1), 0.4045084971874737),\n",
       " (Polygon(n=6, R=1), 0.4330127018922193),\n",
       " (Polygon(n=7, R=1), 0.4504844339512096),\n",
       " (Polygon(n=8, R=1), 0.4619397662556434),\n",
       " (Polygon(n=9, R=1), 0.46984631039295427),\n",
       " (Polygon(n=10, R=1), 0.47552825814757677)]"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[(p, p.area/p.perimeter) for p in polygons]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, looks like our `max_efficiency_polygon` method is working correctly."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As one last thing, we could look at the surface area of our polygons, as the number of vertices become larger and larger.\n",
    "\n",
    "As we have more and more sides, the polygon becomes a closer and closer approximation to a circle. So, the area should get closer and closer to $\\pi$ if we use a circumradius of `1`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "polygons = Polygons(500, 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.1415099708381518"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "polygons[-1].area"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Yep, seems to be working!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
